class Courier {
  #sourceMethods;
  #watcher;
  #sourceData;
  #inbox;
  #sender; // 被监听的状态值
  #domEvents;
  #create;
  #cq;
  #hasViewOnDom;
  constructor(params) {
    this.$store = {}
    this.componentsName = params.name || undefined
    this.components = params.components || {}
    this.rootElSelector = params.el || document
    this.rootEl = undefined,
    this.#sourceData = params.data || {},
    this.data = undefined,
    this.methods = undefined,
    this.#sourceMethods = params.methods || {},
    this.#watcher = params.watcher || {},
    this.isInit = false,
    this.#inbox = params.inbox || {},
    this.#sender = [],
    this.#domEvents = params.domEvents || {}
    this.exampleCode = undefined
    this.#create = params.init || function(){}
    this.#cq =  undefined
    this.isDestroy = false
    this.destroyed = params.afterDestroy || function(){}
    this.unDestroy = params.beforeDestroy || function(){}
    this.#hasViewOnDom = false
    this.#sender = Object.keys(this.#inbox)
  }

  init() {
    if (this.isInit && this.isDestroy === false) {
      console.warn('如需再次初始化需将本Courier销毁后重新初始化。')
      return
    }
    let params = arguments
    
    let _this = this
    this.data = deepcopy(this.#sourceData)
    this.methods = deepcopy(this.#sourceMethods)
    this.data = new Proxy(this.data, {
      set(target, key, value) {
        let oldValue
        if (value === null || value === undefined){
          oldValue = target[key]
        } else if (value.constructor === Array || value.constructor === Object || value.constructor === Function) {
          oldValue = deepcopy(target[key])
        } else {
          oldValue = target[key]
        }
        if (_this.#watcher.hasOwnProperty(key)) {
          _this.#watcher[key](_this.spellExampleData(_this), value, oldValue)
        }
        // Hack 
        // if (Array.isArray(value)) _this.setArrayTrap(value, _this.#watcher[key])
        return Reflect.set(target, key, value);
      }
    })

    for (let d in this.data){
      if (Array.isArray(this.data[d])) {
        this.setArrayTrap(this.data[d], this.#watcher[d])
      }
    }

    this.setMethodsTrap()

    
    this.exampleCode = this.uuid()
    this.#cq = new CourierQuery(this.exampleCode)
    this.rootEl = $(this.rootElSelector).attr({'c-examplecode': this.exampleCode})
    this.bindEvent()
    this.compInspection()
    this.#create(this.spellExampleData(this), this.#cq, params[0], params[1], params[2], params[3], params[4])
    this.#hasViewOnDom = true

    if (!this.isDestroy) {
      this.destroy = new Proxy(this.destroy, {
        apply: function(target,thisArg, argumentsList) {
          _this.unDestroy()
          target(argumentsList[0], _this)
          _this.destroyed()
        }
      })
    }
    this.isInit = true
    this.isDestroy = false
  }

  compInspection() {
    for (let c in this.components) {
      if (!this.components[c].__proto__.hasOwnProperty('cInstall')) {
        throw new Error(`当前${c}子组件不是Courier合法组件!`)
      }
    }
  }

  setArrayTrap(a, watcher) {
    console.log(a, watcher, 'setArrayTrap')
    a.__proto__ = this.coverArrayMutator(watcher, a)
  }

  setMethodsTrap() {
    let _this = this
    for(let m in this.methods) {
      this.methods[m] = new Proxy(this.methods[m], {
        apply: function (target, thisArg, argumentsList) {
          console.log(argumentsList)
          if (argumentsList.length > 4) {
            console.error('Courier的methods自定义函数形参数量不可超过4个.')
            return
          }
          return target(_this.spellExampleData(_this), _this.#cq, argumentsList[0], argumentsList[1], argumentsList[2], argumentsList[3])
        }
      })
    }
  }

  coverArrayMutator(w,d) {
    let _this = this
    let mutatorHandler = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
    let arrayMethod = Object.create(Array.prototype);
    Object.assign(arrayMethod, {indexf: function (index, newValue) {d.splice(index, 1, newValue) }})
    mutatorHandler.forEach(function (method) {
      Object.defineProperty(arrayMethod, method, {
        enumerable: true,
        configurable: true,
        value: function () {
          let oldValue = deepcopy(this)
          Array.prototype[method].apply(this, [...arguments]);
          w(_this.spellExampleData(_this), this, oldValue,  method, [...arguments][arguments.length - 1])
        }
      })
    });
    return arrayMethod
  }




  // 销毁方法
  destroy(isRmView = false, _this) {
    
    _this.isDestroy = true
    _this.#cq.isDestroy = _this.isDestroy
    if (isRmView) { 
      _this.#hasViewOnDom = false
      for (let c in _this.components) {
        _this.components[c].destroy(true)
      }
      _this.rootEl.off().unbind().empty()
    }
    _this.rootEl.off().unbind()
    _this.data = undefined
    _this.setMethodsTrap()
  }


  destroyed() {
    this.afterDestroy()
    console.warn('当前Courier实例已销毁。');
  }
  unDestroy() {
    this.beforeDestroy()
    console.warn('当前Courier实例即将销毁。');
  }


  bindEvent(eventsName = null,addPointerCodeArray = []) {
    // 判断有没有重名方法
    let _this = this
    for (let method in this.#domEvents) {
      if (method.indexOf('_') === -1) continue
      let methodCopy = method
      methodCopy = methodCopy.split('_')
      let methodName = methodCopy
      let methodType = methodCopy[methodCopy.length - 1]
      methodName.pop()
      // let selector = `${this.rootElSelector}[c-examplecode="${_this.exampleCode}"] [c-on="${methodName[0]}"]`
      let selector = `[c-on="${methodName[0]}"]`
      if ($(selector).length > 1) console.warn(`当前组件树"[c-examplecode=${_this.exampleCode}]"注册了多个同名"${methodName[0]}"事件, 不建议使用同名事件!`)
      // this.rootEl.off(methodType, selector).on(methodType, selector, function (event) {
      $(`${this.rootElSelector}[c-examplecode="${_this.exampleCode}"]`).off(methodType, selector).on(methodType, selector, function (event) {
        _this.#domEvents[method](event, this, _this.spellExampleData(_this), _this.#cq)
        event.stopPropagation()
      })
    }
  }

  spellExampleData(sourceThis) {
    return {
      exampleCode: sourceThis.exampleCode, 
      $store: sourceThis.$store, 
      data: sourceThis.data, 
      methods: sourceThis.methods, 
      destroy: sourceThis.destroy, 
      init: sourceThis.init, 
      uuid: sourceThis.uuid, 
      rootEl: sourceThis.rootEl, 
      rootElSelector: sourceThis.rootElSelector, 
      hasViewOnDom: sourceThis.#hasViewOnDom,
      components: sourceThis.components
    }
  }


  isInited() {
    if (this.isDestroy) {
      return { status: true, errMsg: '当前Courier实例已销毁!' }
    }
    if (this.isInit) {
      return { status: true, errMsg: '当前Courier实例初始化完成!' }
    }
    let err = new Error('当前Courier实例尚未初始化!')
    console.error(err)
    return { status: false, errMsg: err }
  }
  postman() {
    return this.#inbox
  }

  cInstall() {
    return this.#sender
  }

  uuid() {
    var s = [];
    var hexDigits = "0123456789abcdef";
    for (var i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4"; 
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); 
    s[8] = s[13] = s[18] = s[23] = "-";

    var uuid = s.join("");
    return uuid;
  }
}


class XCourier {
  #store;
  #mailHub;
  #isInit;
  #submission;
  #name;
  constructor(params) {
    this.#store = params.store,
    this.#mailHub = {},
    this.#isInit = false,
    this.#submission = params.submission,
    this.#name = params.name || undefined
  }

  init() {
    if (this.isInited().status) return
    if (this.#name === undefined || this.#name.constructor !== String || this.#name.length === 0) {
      console.error('请为本XCourier实例声明一个name名，期望的类型为String类型。')
      return
    }
    let _this = this
    for (let i in this.#store) {
      this.#mailHub[i] = []
    }

    for(let s in this.#store) {
      if (Array.isArray(this.#store[s])) {
        this.#store[s].__proto__ = this.coverMutator(s)
      }
    }

    this.#store = new Proxy(this.#store, {
      set(target, key, value) {
          for (let y in _this.#mailHub[key]) {
            let oldValue
            if (target[key] === null || target[key] === undefined){
              oldValue = target[key]
            } else if (target[key].constructor === Array || target[key].constructor === Object || target[key].constructor === Function) {
            oldValue = deepcopy(target[key])
            } else {
              oldValue = target[key]
            }
            let thisCourier = _this.#mailHub[key][y]
            let t = {exampleCode: thisCourier.exampleCode, data: thisCourier.data, methods: thisCourier.methods, destroy: thisCourier.destroy, init: thisCourier.init, uuid: thisCourier.uuid, rootEl: thisCourier.rootEl, rootElSelector: thisCourier.rootElSelector}
            if (!thisCourier.isDestroy && thisCourier.isInit) thisCourier.postman()[key](t, value, oldValue)
          }

        return Reflect.set(target, key, value);

      }
    })
    this.#isInit = true
    return this
  }

  spellExampleData(sourceThis) {
    return {exampleCode: sourceThis.exampleCode, data: sourceThis.data, methods: sourceThis.methods, destroy: sourceThis.destroy, init: sourceThis.init, uuid: sourceThis.uuid, rootEl: sourceThis.rootEl, rootElSelector: sourceThis.rootElSelector}
  }

  
  coverMutator(s) {
    let _this = this
    let mutatorHandler = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
    let arrayMethod = Object.create(Array.prototype);
    Object.assign(arrayMethod, {indexf: function (index, newValue) {_this.#store[s].splice(index, 1, newValue) }})
    mutatorHandler.forEach(function (method) {
      Object.defineProperty(arrayMethod, method, {
        enumerable: true,
        configurable: true,
        value: function () {
          let oldValue = deepcopy(this)
          Array.prototype[method].apply(this, [...arguments]);
          for (let y in _this.#mailHub[s]) {
            let thisCourier = _this.#mailHub[s][y]
            let t = {exampleCode: thisCourier.exampleCode, data: thisCourier.data, methods: thisCourier.methods, destroy: thisCourier.destroy, init: thisCourier.init, uuid: thisCourier.uuid, rootEl: thisCourier.rootEl, rootElSelector: thisCourier.rootElSelector}
            if (!thisCourier.isDestroy && thisCourier.isInit) thisCourier.postman()[s](t, oldValue, this, method, [...arguments][arguments.length - 1])
          }
        }
      })
    });
    return arrayMethod
  }

  getter(key) {
    if (!this.isInited().status) return this.isInited().errMsg
    return this.#store[key]
  }

  trigger(t) {
    let v
    for(let i in t) {
      if (t[i].constructor === Array || t[i].constructor === Object || t[i].constructor === Function){
        v = deepcopy(t[i])
      } else {
        v = t[i]
      }
      if (!this.#submission.hasOwnProperty(i)) throw new Error(`当前XCourier容器不存在${i}状态!`)
      this.#submission[i](this.#store, v)
    }
  }

  isInited(){
    if (this.#isInit) {
      return { status: true, errMsg: '当前XCourier实例初始化完成!'}
    }  
    let err =  new Error('当前XCourier实例尚未初始化!')
    return { status: false, errMsg: err}
  }

  extend(instanceParams) {
    if (instanceParams.constructor === Object) {
      for (let i of instance) {
        this.exFn(instance)
      }
    } else {
      this.exFn(instanceParams)
    }
    return this

  }

  exFn(instance) {
    if (!instance.__proto__.hasOwnProperty('cInstall')) return new Error(`当前${instance}不是XCourier.js合法扩展方法!`)
    let sender = instance.cInstall()
    for (let i of sender) {
      if (this.#mailHub.hasOwnProperty(i)) {  
        this.#mailHub[i].push(instance) 
        // toFix
        // if (this.#mailHub[i].length === 0) this.#mailHub[i].push(instance) 
        // let a = this.#mailHub[i].every(mItem => mItem.exampleCode === instance.exampleCode)
        // if (this.#mailHub[i].length > 0 && a) this.#mailHub[i].push(instance)  
      }
    }
    if (!instance.$store.hasOwnProperty(this.#name)) {
      instance.$store[this.#name] = this
    }
    
  }
}


function deepClone(source) {
  let targetObj = source.constructor === Array ? [] : {}
  for(let keys in source){
    if(source.hasOwnProperty(keys)) {
      if(source[keys] && typeof source[keys] === 'object') { 
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      } else { 
        targetObj[keys] = source[keys];
      }
    }
  }
  global.funckSource = source

  return targetObj;
}


class CourierQuery {
  constructor(belongCode) {
    this.belongCode = belongCode
    this.selector = undefined
    this.isDestroy = false
  }
  _$(el) { 
    if (el === 'root') {
      this.selector = $(`[c-examplecode='${this.belongCode}']`)
    } else {
      this.selector = $(`[c-examplecode='${this.belongCode}'] ${el}`)
    }
    if (this.selector.length === 0) throw new Error(`未发现当前选择器"${el}"所对应的DOM元素`)
    return this
  }
  _empty() {
    if (this.isDestroy) return this
    this.selector = this.selector.empty()
    return this
  }
  _text(text){
    if (this.isDestroy) return this
    this.selector = this.selector.text(text)
    return this
  }
  _append(template) {
    if (this.isDestroy) return this
    return this.selector.append(template)
  }
  _find(findSelector) {
    if (this.isDestroy) return this
    this.selector = this.selector.find(findSelector)
    return this
  }
  _before(template) {
    if (this.isDestroy) return this
    this.selector = this.selector.before(template)
    return this
  }
  _append(template) {
    if (this.isDestroy) return this
    this.selector = this.selector.append(template)
    return this
  }
  _prepend(text){
    if (this.isDestroy) return this
    this.selector = this.selector.prepend(text)
    return this
  }
  _remove(){
    if (this.isDestroy) return this
    this.selector = this.selector.remove()
    return this
  }
  _addClass(className) {
    if (this.isDestroy) return this
    this.selector = this.selector.addClass(className)
    return this
  }
  _removeClass(className) {
    if (this.isDestroy) return this
    this.selector = this.selector.removeClass(className)
    return this
  }
  _toggleClass(className) {
    if (this.isDestroy) return this
    this.selector = this.selector.toggleClass(className)
    return this
  }
  _css(css) {
    if (this.isDestroy) return this
    this.selector = this.selector.css(css)
    return this
  }
  _val(value) {
    if (this.isDestroy) return this
    if (value === undefined) {
      return this.selector.val()
    } else {
      this.selector = this.selector.val(value)
      return this
    }
  }
  _index() {
    if (this.isDestroy) return this
    return this.selector.index()
  }
  _attr(attribute) {
    if (this.isDestroy) return this
    if (attribute === undefined) {
      return this.selector.attr(attribute)
    } else {
      this.selector = this.selector.attr(attribute)
      return this
    }
  }
  _siblings(className) {
    if (this.isDestroy) return this
    this.selector = this.selector.siblings(className)
    return this
  }

}

module.exports = { Courier, XCourier }